/*     */ package com.zimbra.cs.volume;
/*     */ 
/*     */ import com.google.common.base.Strings;
/*     */ import com.google.common.collect.ImmutableList;
/*     */ import com.google.common.collect.Lists;
/*     */ import com.google.common.collect.Maps;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.db.DbPool;
/*     */ import com.zimbra.cs.db.DbPool.DbConnection;
/*     */ import com.zimbra.cs.db.DbVolume;
/*     */ import com.zimbra.cs.db.DbVolume.CurrentVolumes;
/*     */ import com.zimbra.cs.redolog.op.CreateVolume;
/*     */ import com.zimbra.cs.redolog.op.DeleteVolume;
/*     */ import com.zimbra.cs.redolog.op.ModifyVolume;
/*     */ import com.zimbra.cs.redolog.op.RedoableOp;
/*     */ import com.zimbra.cs.redolog.op.SetCurrentVolume;
/*     */ import com.zimbra.cs.store.IncomingDirectory;
/*     */ import java.io.File;
/*     */ import java.util.List;
/*     */ import java.util.Map;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public final class VolumeManager
/*     */ {
/*  41 */   private static final VolumeManager SINGLETON = new VolumeManager();
/*     */   
/*  43 */   private final Map<Short, Volume> id2volume = Maps.newHashMap();
/*     */   private Volume currentMessageVolume;
/*     */   private Volume currentSecondaryMessageVolume;
/*     */   private Volume currentIndexVolume;
/*     */   
/*     */   private VolumeManager() {
/*     */     try {
/*  50 */       load();
/*     */     } catch (ServiceException e) {
/*  52 */       ZimbraLog.store.error("Failed to initialize VolumeManager", e);
/*     */     }
/*     */   }
/*     */   
/*     */   public static VolumeManager getInstance() {
/*  57 */     return SINGLETON;
/*     */   }
/*     */   
/*     */   private void load() throws ServiceException {
/*  61 */     DbPool.DbConnection conn = DbPool.getConnection();
/*     */     try {
/*  63 */       this.id2volume.putAll(DbVolume.getAll(conn));
/*  64 */       updateSweptDirectories();
/*     */       
/*  66 */       DbVolume.CurrentVolumes current = DbVolume.getCurrentVolumes(conn);
/*  67 */       if (current == null) {
/*  68 */         ZimbraLog.store.warn("Missing current volumes info from configuration");
/*     */       }
/*     */       else
/*     */       {
/*  72 */         if (current.msgVolId != -2) {
/*  73 */           this.currentMessageVolume = ((Volume)this.id2volume.get(Short.valueOf(current.msgVolId)));
/*  74 */           if (this.currentMessageVolume == null) {
/*  75 */             ZimbraLog.store.warn("Unknown current message volume id=%d", new Object[] { Short.valueOf(current.msgVolId) });
/*     */           }
/*     */         }
/*     */         
/*  79 */         if (current.secondaryMsgVolId != -2) {
/*  80 */           this.currentSecondaryMessageVolume = ((Volume)this.id2volume.get(Short.valueOf(current.secondaryMsgVolId)));
/*  81 */           if (this.currentSecondaryMessageVolume == null) {
/*  82 */             ZimbraLog.store.warn("Unknown current secondary message volume id=%d", new Object[] { Short.valueOf(current.secondaryMsgVolId) });
/*     */           }
/*     */         }
/*     */         
/*  86 */         if (current.indexVolId != -2) {
/*  87 */           this.currentIndexVolume = ((Volume)this.id2volume.get(Short.valueOf(current.indexVolId)));
/*  88 */           if (this.currentIndexVolume == null)
/*  89 */             ZimbraLog.store.warn("Unknown current index volume id=%d", new Object[] { Short.valueOf(current.indexVolId) });
/*     */         }
/*     */       }
/*     */     } finally {
/*  93 */       conn.closeQuietly();
/*     */     }
/*     */   }
/*     */   
/*     */   private void updateSweptDirectories() {
/*  98 */     List<IncomingDirectory> dirs = Lists.newArrayListWithCapacity(this.id2volume.size());
/*  99 */     for (Volume vol : this.id2volume.values()) {
/* 100 */       IncomingDirectory dir = vol.getIncomingDirectory();
/* 101 */       if (dir != null) {
/* 102 */         dirs.add(dir);
/*     */       }
/*     */     }
/* 105 */     IncomingDirectory.setSweptDirectories(dirs);
/*     */   }
/*     */   
/*     */   public Volume create(Volume volume) throws ServiceException {
/* 109 */     return create(volume, false);
/*     */   }
/*     */   
/*     */   public Volume create(Volume volume, boolean noRedo) throws ServiceException {
/* 113 */     CreateVolume redoRecorder = null;
/* 114 */     if (!noRedo) {
/* 115 */       redoRecorder = new CreateVolume(volume);
/* 116 */       redoRecorder.start(System.currentTimeMillis());
/*     */     }
/*     */     
/* 119 */     boolean success = false;
/* 120 */     DbPool.DbConnection conn = DbPool.getConnection();
/*     */     try {
/* 122 */       volume = DbVolume.create(conn, volume);
/* 123 */       success = true;
/* 124 */       if (!noRedo) {
/* 125 */         redoRecorder.setId(volume.getId());
/* 126 */         redoRecorder.log();
/*     */       }
/* 128 */       return volume;
/*     */     } finally {
/* 130 */       endTransaction(success, conn, redoRecorder);
/* 131 */       if (success) {
/* 132 */         synchronized (this) {
/* 133 */           this.id2volume.put(Short.valueOf(volume.getId()), volume);
/* 134 */           updateSweptDirectories();
/*     */         }
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   public Volume update(Volume update) throws ServiceException {
/* 141 */     return update(update, false);
/*     */   }
/*     */   
/*     */   public Volume update(Volume update, boolean noRedo)
/*     */     throws ServiceException
/*     */   {
/* 147 */     Volume vol = getVolume(update.getId());
/* 148 */     if ((update.getType() != vol.getType()) && 
/* 149 */       (isCurrent(vol))) {
/* 150 */       throw VolumeServiceException.CANNOT_CHANGE_TYPE_OF_CURRVOL(vol, update.getType());
/*     */     }
/*     */     
/*     */ 
/* 154 */     ModifyVolume redoRecorder = null;
/* 155 */     if (!noRedo) {
/* 156 */       redoRecorder = new ModifyVolume(update);
/* 157 */       redoRecorder.start(System.currentTimeMillis());
/*     */     }
/*     */     
/* 160 */     boolean success = false;
/* 161 */     DbPool.DbConnection conn = DbPool.getConnection();
/*     */     try {
/* 163 */       update = DbVolume.update(conn, update);
/* 164 */       success = true;
/* 165 */       if (!noRedo) {
/* 166 */         redoRecorder.log();
/*     */       }
/* 168 */       return update;
/*     */     } finally {
/* 170 */       endTransaction(success, conn, redoRecorder);
/* 171 */       if (success) {
/* 172 */         synchronized (this) {
/* 173 */           this.id2volume.put(Short.valueOf(update.getId()), update);
/* 174 */           updateSweptDirectories();
/* 175 */           if (isCurrent(vol)) {
/* 176 */             updateCurrentVolumeRefs(update, update.getType());
/*     */           }
/*     */         }
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   public boolean delete(short id) throws ServiceException {
/* 184 */     return delete(id, false);
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */   public boolean delete(short id, boolean noRedo)
/*     */     throws ServiceException
/*     */   {
/* 193 */     DeleteVolume redoRecorder = null;
/* 194 */     if (!noRedo) {
/* 195 */       redoRecorder = new DeleteVolume(id);
/* 196 */       redoRecorder.start(System.currentTimeMillis());
/*     */     }
/*     */     
/*     */ 
/* 200 */     synchronized (this) {
/* 201 */       if ((this.currentMessageVolume != null) && (id == this.currentMessageVolume.getId())) {
/* 202 */         throw VolumeServiceException.CANNOT_DELETE_CURRVOL(id, "message");
/*     */       }
/* 204 */       if ((this.currentSecondaryMessageVolume != null) && (id == this.currentSecondaryMessageVolume.getId())) {
/* 205 */         throw VolumeServiceException.CANNOT_DELETE_CURRVOL(id, "secondary message");
/*     */       }
/* 207 */       if ((this.currentIndexVolume != null) && (id == this.currentIndexVolume.getId())) {
/* 208 */         throw VolumeServiceException.CANNOT_DELETE_CURRVOL(id, "index");
/*     */       }
/* 210 */       Volume vol = getVolume(id);
/* 211 */       if ((vol.getType() == 1) || (vol.getType() == 2)) {
/* 212 */         File path = new File(vol.getRootPath());
/* 213 */         String[] files = path.list();
/* 214 */         if ((files != null) && (files.length > 0))
/*     */         {
/* 216 */           DbPool.DbConnection conn = DbPool.getConnection();
/*     */           try {
/* 218 */             if (DbVolume.isVolumeReferenced(conn, id)) {
/* 219 */               ZimbraLog.store.warn("volume %d referenced by mail_item cannot be deleted", new Object[] { Short.valueOf(id) });
/* 220 */               throw VolumeServiceException.CANNOT_DELETE_VOLUME_IN_USE(id, null);
/*     */             }
/*     */           } finally {
/* 223 */             conn.closeQuietly();
/*     */           }
/*     */         }
/*     */       }
/*     */     }
/*     */     
/* 229 */     boolean success = false;
/* 230 */     DbPool.DbConnection conn = DbPool.getConnection();
/*     */     try {
/* 232 */       boolean deleted = DbVolume.delete(conn, id);
/* 233 */       if (!noRedo) {
/* 234 */         redoRecorder.log();
/*     */       }
/* 236 */       synchronized (this) {
/* 237 */         this.id2volume.remove(Short.valueOf(id));
/* 238 */         updateSweptDirectories();
/*     */       }
/* 240 */       success = true;
/* 241 */       return deleted;
/*     */     } finally {
/* 243 */       endTransaction(success, conn, redoRecorder);
/*     */     }
/*     */   }
/*     */   
/*     */   public Volume getVolume(String id) throws ServiceException {
/*     */     try {
/* 249 */       return getVolume(Short.parseShort(id));
/*     */     } catch (NumberFormatException e) {
/* 251 */       throw ServiceException.INVALID_REQUEST("invalid volume ID: " + id, e);
/*     */     }
/*     */   }
/*     */   
/*     */   public Volume getVolume(short id) throws ServiceException {
/* 256 */     Volume vol = null;
/* 257 */     synchronized (this) {
/* 258 */       vol = (Volume)this.id2volume.get(Short.valueOf(id));
/*     */     }
/* 260 */     if (vol != null) {
/* 261 */       return vol;
/*     */     }
/*     */     
/* 264 */     DbPool.DbConnection conn = DbPool.getConnection();
/*     */     try {
/* 266 */       vol = DbVolume.get(conn, id);
/* 267 */       if (vol != null) {
/* 268 */         synchronized (this) {
/* 269 */           this.id2volume.put(Short.valueOf(id), vol);
/*     */         }
/* 271 */         return vol;
/*     */       }
/* 273 */       throw VolumeServiceException.NO_SUCH_VOLUME(id);
/*     */     }
/*     */     finally {
/* 276 */       conn.closeQuietly();
/*     */     }
/*     */   }
/*     */   
/*     */   public synchronized List<Volume> getAllVolumes() {
/* 281 */     return ImmutableList.copyOf(this.id2volume.values());
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   public synchronized Volume getCurrentMessageVolume()
/*     */   {
/* 291 */     return this.currentMessageVolume;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   public synchronized Volume getCurrentSecondaryMessageVolume()
/*     */   {
/* 301 */     return this.currentSecondaryMessageVolume;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   public synchronized Volume getCurrentIndexVolume()
/*     */   {
/* 311 */     return this.currentIndexVolume;
/*     */   }
/*     */   
/*     */   public synchronized boolean isCurrent(Volume vol) {
/* 315 */     return (this.currentMessageVolume == vol) || (this.currentSecondaryMessageVolume == vol) || (this.currentIndexVolume == vol);
/*     */   }
/*     */   
/*     */   public void setCurrentVolume(short type, short id) throws ServiceException {
/* 319 */     setCurrentVolume(type, id, false);
/*     */   }
/*     */   
/*     */ 
/*     */   public void setCurrentVolume(short type, short id, boolean noRedo)
/*     */     throws ServiceException
/*     */   {
/* 326 */     SetCurrentVolume redoRecorder = null;
/* 327 */     if (!noRedo) {
/* 328 */       redoRecorder = new SetCurrentVolume(type, id);
/* 329 */       redoRecorder.start(System.currentTimeMillis());
/*     */     }
/*     */     
/* 332 */     Volume vol = null;
/* 333 */     if (id != -2) {
/* 334 */       vol = getVolume(id);
/* 335 */       if (vol.getType() != type) {
/* 336 */         throw VolumeServiceException.WRONG_TYPE_CURRVOL(id, type);
/*     */       }
/*     */     }
/*     */     
/* 340 */     boolean success = false;
/* 341 */     DbPool.DbConnection conn = DbPool.getConnection();
/*     */     try {
/* 343 */       DbVolume.updateCurrentVolume(conn, type, id);
/* 344 */       updateCurrentVolumeRefs(vol, type);
/* 345 */       success = true;
/* 346 */       if (!noRedo) {
/* 347 */         redoRecorder.log();
/*     */       }
/*     */     } finally {
/* 350 */       endTransaction(success, conn, redoRecorder);
/*     */     }
/*     */   }
/*     */   
/*     */   private void updateCurrentVolumeRefs(Volume vol, short type) {
/* 355 */     synchronized (this) {
/* 356 */       switch (type) {
/*     */       case 1: 
/* 358 */         this.currentMessageVolume = vol;
/* 359 */         break;
/*     */       case 2: 
/* 361 */         this.currentSecondaryMessageVolume = vol;
/* 362 */         break;
/*     */       case 10: 
/* 364 */         this.currentIndexVolume = vol;
/* 365 */         break;
/*     */       default: 
/* 367 */         throw new IllegalArgumentException("invalid volume type: " + type);
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private void endTransaction(boolean success, DbPool.DbConnection conn, RedoableOp redoRecorder) throws ServiceException
/*     */   {
/* 374 */     if (success) {
/* 375 */       if (conn != null) {
/* 376 */         conn.commit();
/* 377 */         conn.closeQuietly();
/*     */       }
/* 379 */       if (redoRecorder != null) {
/* 380 */         redoRecorder.commit();
/*     */       }
/*     */     } else {
/* 383 */       if (conn != null) {
/* 384 */         conn.rollback();
/* 385 */         conn.closeQuietly();
/*     */       }
/* 387 */       if (redoRecorder != null) {
/* 388 */         redoRecorder.abort();
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   void validatePath(String path) throws ServiceException {
/* 394 */     if (Strings.isNullOrEmpty(path)) {
/* 395 */       throw VolumeServiceException.INVALID_REQUEST("Missing volume path");
/*     */     }
/* 397 */     path = Volume.getAbsolutePath(path);
/*     */     
/*     */ 
/*     */ 
/* 401 */     String pathSlashed = path.replaceAll("\\\\", "/");
/* 402 */     synchronized (this) {
/* 403 */       for (Volume vol : getAllVolumes()) {
/* 404 */         String vpath = vol.getRootPath().replaceAll("\\\\", "/");
/* 405 */         int len = vpath.length();
/* 406 */         if ((len > 0) && (vpath.charAt(len - 1) != '/')) {
/* 407 */           vpath = vpath + "/";
/*     */         }
/* 409 */         if (pathSlashed.indexOf(vpath) == 0) {
/* 410 */           throw VolumeServiceException.SUBDIR_OF_ANOTHER_VOLUME(path, vol);
/*     */         }
/*     */       }
/*     */     }
/*     */     
/* 415 */     File root = new File(path);
/* 416 */     if ((!root.exists()) || (!root.isDirectory()) || (!root.canWrite())) {
/* 417 */       throw VolumeServiceException.NO_SUCH_PATH(path);
/*     */     }
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/volume/VolumeManager.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */